import type { VcComponentInternalInstance, VcComponentPublicInstance, VcReadyObject } from '@vue-cesium/utils/types'
/*
 * @Author: zouyaoji@https://github.com/zouyaoji
 * @Date: 2021-10-27 15:54:11
 * @LastEditTime: 2022-09-25 16:19:47
 * @LastEditors: zouyaoji
 * @Description:
 * @FilePath: \vue-cesium@next\packages\components\controls\selection-indicator\index.ts
 */
import type { PropType, Ref } from 'vue'
import type Feature from './Feature'
import type PickedFeatures from './PickedFeatures'
import { useCommon } from '@vue-cesium/composables'
import { commonEmits } from '@vue-cesium/utils/emits'
import { $ } from '@vue-cesium/utils/private/vm'
import { defineComponent, getCurrentInstance, h } from 'vue'
import useSelectionIndicatior from './use-selection-indicatior'

export const selectionIndicatorProps = {
  show: {
    type: Boolean,
    default: true
  },
  width: {
    type: Number,
    default: 50
  },
  height: {
    type: Number,
    default: 50
  },
  allowFeatureInfoRequests: {
    type: Boolean,
    default: true
  },
  includeImageryIds: {
    type: Array as PropType<string[]>,
    default: () => []
  },
  excludeImageryIds: {
    type: Array as PropType<string[]>,
    default: () => []
  },
  limit: {
    type: Number,
    default: 25
  }
}
const emits = {
  ...commonEmits,
  pickEvt: (evt: Feature | Cesium.Entity) => true
}
export default defineComponent({
  name: 'VcSelectionIndicator',
  props: selectionIndicatorProps,
  emits,
  setup(props: VcSelectionIndicatorProps, ctx) {
    // state
    const instance = getCurrentInstance() as VcComponentInternalInstance
    instance.cesiumClass = 'VcSelectionIndicator'
    instance.cesiumEvents = []
    const commonState = useCommon(props, ctx, instance)
    if (commonState === void 0) {
      return
    }
    const { $services } = commonState

    let pickScreenSpaceEventHandler: Cesium.ScreenSpaceEventHandler
    const useSelectionIndicatiorState = useSelectionIndicatior(instance, props, $services)

    // methods
    instance.createCesiumObject = async () => {
      const { viewer } = $services
      const viewerElement = (viewer as any)._element
      viewerElement.appendChild($(useSelectionIndicatiorState.rootRef))
      return $(useSelectionIndicatiorState.rootRef)
    }

    instance.mount = async () => {
      const { viewer } = $services
      const { ScreenSpaceEventHandler, ScreenSpaceEventType } = Cesium

      pickScreenSpaceEventHandler = new ScreenSpaceEventHandler(viewer.canvas)
      pickScreenSpaceEventHandler.setInputAction((movement) => {
        useSelectionIndicatiorState.pickFromScreenPosition(movement.position)
      }, ScreenSpaceEventType.LEFT_CLICK)

      viewer.scene.postRender.addEventListener(useSelectionIndicatiorState.onPostRender)

      return true
    }

    instance.unmount = async () => {
      const { viewer } = $services
      const viewerElement = (viewer as any)._element
      viewerElement.contains($(useSelectionIndicatiorState.rootRef)) && viewerElement.removeChild($(useSelectionIndicatiorState.rootRef))

      viewer.scene.postRender.removeEventListener(useSelectionIndicatiorState.onPostRender)

      pickScreenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
      pickScreenSpaceEventHandler.destroy()
      ;(pickScreenSpaceEventHandler as any) = undefined

      return true
    }

    return () => {
      return h(
        'div',
        {
          ref: useSelectionIndicatiorState.rootRef,
          class: 'vc-selection-indicator',
          style: useSelectionIndicatiorState.rootStyle
        },
        h('img', {
          src: '',
          width: props.width,
          height: props.height
        })
      )
    }
  }
})

export type VcSelectionIndicatorEmits = typeof emits
export interface VcSelectionIndicatorProps {
  /**
   * Specify whether the selection indicator is visible.
   * Default value: true
   */
  show?: boolean
  /**
   * Specify the width of the selection indicator.
   * Default value: 50
   */
  width?: number
  /**
   * Specify the height of the selection indicator.
   * Default value: 50
   */
  height?: number
  /**
   * Asynchronously determines the imagery layer features that are intersected by a pick ray.
   * Default value: true
   */
  allowFeatureInfoRequests?: boolean
  /**
   * Specify that picking layers only work within the contained id array.
   */
  includeImageryIds?: string[]
  /**
   * Specify an array of ids to ignore when picking up an imagerylayer.
   */
  excludeImageryIds?: string[]
  /**
   * Specify the maximum number of picked objects.
   * Default value: 25
   */
  limit?: number
  /**
   * Triggers before the VcSelectionIndicator is loaded.
   * @param instance
   */
  onBeforeLoad?: (instance: VcComponentInternalInstance) => void
  /**
   * Triggers when the VcSelectionIndicator is successfully loaded.
   */
  onReady?: (readyObject: VcReadyObject) => void
  /**
   * Triggers when the component load failed.
   */
  onUnready?: (e: any) => void
  /**
   * Triggers when the VcSelectionIndicator is destroyed.
   */
  onDestroyed?: (instance: VcComponentInternalInstance) => void
}

export interface VcSelectionIndicatorRef extends VcComponentPublicInstance<VcSelectionIndicatorProps> {
  /**
   * A function that converts the world position of an object to a screen space position.
   */
  computeScreenSpacePosition: (position: Cesium.Cartesian3, result: Cesium.Cartesian2) => Cesium.Cartesian2
  /**
   * Updates the view of the selection indicator to match the position and content properties of the view model. This function should be called as part of the render loop.
   */
  update: () => void
  /**
   * Animate the indicator to draw attention to the selection.
   */
  animateAppear: () => void
  /**
   * Animate the indicator to release the selection.
   */
  animateDepart: () => void
  /**
   * Get the picked features.
   */
  getPickedFeatures: () => PickedFeatures
  /**
   * Get or set the selected feature.
   */
  selectedFeature: Feature | Cesium.Entity
  /**
   * Gets or sets the world position of the object for which to display the selection indicator.
   */
  position: Ref<Cesium.Cartesian3>
}
